package reporte;

import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;

import productosYPresentaciones.Producto;
import reporte.filtros.Filtro;
import ventas.Venta;
import clientes.Ticket;
import exceptions.ParametrosMalColocadosException;

public class BuilderFiltroVentaTest {

	public Venta venta1,venta2,venta3,venta4,venta5;
	public List<Venta> lista;
	public DateTime fechabase;
	public Producto productoABuscar, productoSolitario;
	private BuilderFiltroVenta builder;

	/**
	 * Crea todos los elementos para realizar el test
	 * @throws Exception
	 */
	@Before
	public void setUp() throws Exception {
		lista = new ArrayList<Venta>();
		venta1 = mock( Venta.class );
		venta2 = mock( Venta.class );
		venta3 = mock( Venta.class );
		venta4 = mock( Venta.class );
		venta5 = mock( Venta.class );
		
		lista.add(venta1);
		lista.add(venta2);
		lista.add(venta3);
		lista.add(venta4);
		lista.add(venta5);
		
		//Fundamental para hacer funcionar los test
		this.inicializarFechas();
		this.inicializarMontos();
		this.inicializarProductos();
		this.inicializarVentasPendientes();
	}

	protected void inicializarVentasPendientes() {
		//Se establecen si son ventas con envio
		when( venta1.soyUnaVentaConEnvio() ).thenReturn(true);
		when( venta2.soyUnaVentaConEnvio() ).thenReturn(false);
		when( venta3.soyUnaVentaConEnvio() ).thenReturn(false);
		when( venta4.soyUnaVentaConEnvio() ).thenReturn(true);
		when( venta5.soyUnaVentaConEnvio() ).thenReturn(false);
	}
	
	/**
	 * Se crean todos los datos necesarios para realizar test con fechas
	 */
	protected void inicializarFechas(){
		//Fechas de las ventas
		fechabase =  new DateTime();
		DateTime fechav1 = fechabase.plusMonths(1),
				fechav2 = fechabase.plusMonths(2),
				fechav3 = fechabase.plusMonths(3),
				fechav4 = fechabase.plusMonths(5),
				fechav5 = fechabase.plusMonths(10);
		//Basicamente estan ordenadas cronologicamente

		//Se establecen las fechas de las ventas
		when( venta1.getFechaDeCreacionDeLaVenta() ).thenReturn(fechav1);
		when( venta2.getFechaDeCreacionDeLaVenta() ).thenReturn(fechav2);
		when( venta3.getFechaDeCreacionDeLaVenta() ).thenReturn(fechav3);
		when( venta4.getFechaDeCreacionDeLaVenta() ).thenReturn(fechav4);
		when( venta5.getFechaDeCreacionDeLaVenta() ).thenReturn(fechav5);
	}
	
	/**
	 * Se crean todos los datos necesarios para realizar test con montos
	 */
	protected void inicializarMontos(){
		//Tickets(para montos) de las ventas
		Ticket rc1 = mock(Ticket.class),
				rc2 = mock(Ticket.class),
				rc3 = mock(Ticket.class),
				rc4 = mock(Ticket.class),
				rc5 = mock(Ticket.class);
		

		//Se establecen los montos de las ventas
		when( venta1.getTicketDeVenta() ).thenReturn(rc1);
		when( venta2.getTicketDeVenta() ).thenReturn(rc2);
		when( venta3.getTicketDeVenta() ).thenReturn(rc3);
		when( venta4.getTicketDeVenta() ).thenReturn(rc4);
		when( venta5.getTicketDeVenta() ).thenReturn(rc5);
		when(rc1.getPrecio()).thenReturn(500f);
		when(rc2.getPrecio()).thenReturn(40f);
		when(rc3.getPrecio()).thenReturn(2000f);
		when(rc4.getPrecio()).thenReturn(5f);
		when(rc5.getPrecio()).thenReturn(125f);
	}
	
	/**
	 * Se crean todos los datos necesarios para realizar test con productos
	 */
	protected void inicializarProductos(){
		//Se establecen los productos participantes del test
		productoABuscar = mock( Producto.class );
		productoSolitario = mock( Producto.class );
		
		//Se establecen la listas de productos 
		Map<Producto,Integer> listaConProductoABuscar = mock( Map.class ),
				listaConProductoSolitario = mock( Map.class ),
				listaConAmbosProductos = mock( Map.class );

		//Que productos contiene cada lista de productos
		when( listaConProductoABuscar.containsKey(productoABuscar) ).thenReturn(true);
		when( listaConProductoABuscar.containsKey(productoSolitario) ).thenReturn(false);
		when( listaConProductoSolitario.containsKey(productoABuscar) ).thenReturn(false);
		when( listaConProductoSolitario.containsKey(productoSolitario) ).thenReturn(true);
		when( listaConAmbosProductos.containsKey(productoABuscar) ).thenReturn(true);
		when( listaConAmbosProductos.containsKey(productoSolitario) ).thenReturn(true);


		//Se establecen las listas de compras de las ventas
		when( venta1.getProductosComprados() ).thenReturn( listaConProductoABuscar );
		when( venta2.getProductosComprados() ).thenReturn( listaConAmbosProductos );
		when( venta3.getProductosComprados() ).thenReturn( listaConProductoSolitario );
		when( venta4.getProductosComprados() ).thenReturn( listaConProductoSolitario );
		when( venta5.getProductosComprados() ).thenReturn( listaConAmbosProductos );
	}

	@Test
	public void testFiltroTrue() {
		//Verifico el contenido
		BuilderFiltroVenta builder = BuilderFiltroVenta.filtroTrue();
		assertArrayEquals(lista.toArray(),builder.build().filtrar(lista).toArray());
	}

	@Test
	public void testFiltroDesdeFecha() {
		//Verifico el contenido
		BuilderFiltroVenta builder = BuilderFiltroVenta.filtroDesdeFecha(fechabase.plusMonths(4));
		List<Venta> ventas = builder.build().filtrar(lista);
		assertEquals( 2, ventas.size() );
		assertTrue( ventas.contains(venta4) );
		assertTrue( ventas.contains(venta5) );
	}

	@Test
	public void testFiltroHastaFecha() {
		//Verifico el contenido
		BuilderFiltroVenta builder = BuilderFiltroVenta.filtroHastaFecha(fechabase.plusMonths(4));
		List<Venta> ventas = builder.build().filtrar(lista);
		assertEquals( 3, ventas.size() );
		assertTrue( ventas.contains(venta1) );
		assertTrue( ventas.contains(venta2) );
		assertTrue( ventas.contains(venta3) );
	}

	@Test
	public void testFiltroDesdeMonto() {
		//Verifico el contenido
		BuilderFiltroVenta builder = BuilderFiltroVenta.filtroDesdeMonto(100f);
		List<Venta> ventas = builder.build().filtrar(lista);
		assertEquals( 3, ventas.size() );
		assertTrue( ventas.contains(venta1) );
		assertTrue( ventas.contains(venta3) );
		assertTrue( ventas.contains(venta5) );
	}

	@Test
	public void testFiltroHastaMonto() {
		//Verifico el contenido
		BuilderFiltroVenta builder = BuilderFiltroVenta.filtroHastaMonto(1000f);
		List<Venta> ventas = builder.build().filtrar(lista);
		assertEquals( 4, ventas.size() );
		assertTrue( ventas.contains(venta1) );
		assertTrue( ventas.contains(venta2) );
		assertTrue( ventas.contains(venta5) );
	}

	@Test
	public void testFiltroPorProducto() {
		//Verifico el contenido
		BuilderFiltroVenta builder = BuilderFiltroVenta.filtroPorProducto(productoABuscar);
		List<Venta> ventas = builder.build().filtrar(lista);
		assertEquals( 3, ventas.size() );
		assertTrue( ventas.contains(venta1) );
		assertTrue( ventas.contains(venta2) );
		assertTrue( ventas.contains(venta5) );
	}

	@Test
	public void testFiltroEntreFechas() throws ParametrosMalColocadosException {
		//Verifico el contenido
		BuilderFiltroVenta builder = BuilderFiltroVenta.filtroEntreFechas
				(fechabase.plusMonths(1),fechabase.plusMonths(7));
		List<Venta> ventas = builder.build().filtrar(lista);
		assertEquals( 3, ventas.size() );
		assertTrue( ventas.contains(venta2) );
		assertTrue( ventas.contains(venta3) );
		assertTrue( ventas.contains(venta4) );
	}
	
	@Test(expected = ParametrosMalColocadosException.class)
	public void testFiltroEntreFechasLanzaException() throws ParametrosMalColocadosException {
		//Verifico el contenido
		BuilderFiltroVenta builder = BuilderFiltroVenta.filtroEntreFechas
				(fechabase.plusMonths(7),fechabase.plusMonths(1));

	}

	@Test
	public void testFiltroEntreMontos() throws ParametrosMalColocadosException {
		//Verifico el contenido
		BuilderFiltroVenta builder = BuilderFiltroVenta.filtroEntreMontos(100f, 1000f);
		List<Venta> ventas = builder.build().filtrar(lista);
		assertEquals( 2, ventas.size() );
		assertTrue( ventas.contains(venta1) );
		assertTrue( ventas.contains(venta5) );
	}
	
	@Test(expected = ParametrosMalColocadosException.class)
	public void testFiltroEntreMontosLanzaExcepcion() throws ParametrosMalColocadosException {
		//Verifico el contenido
		BuilderFiltroVenta builder = BuilderFiltroVenta.filtroEntreMontos(1000f, 100f);
	}

	@Test
	public void testFiltroConEnvios() {
		//Verifico el contenido
		BuilderFiltroVenta builder = BuilderFiltroVenta.filtroConEnvios();
		List<Venta> ventas = builder.build().filtrar(lista);
		assertEquals( 2, ventas.size() );
		assertTrue( ventas.contains(venta1) );
		assertTrue( ventas.contains(venta4) );
	}

	@Test
	public void testNeg() throws ParametrosMalColocadosException {
		//Verifico el contenido
		BuilderFiltroVenta builder = BuilderFiltroVenta.filtroEntreMontos(100f, 1000f);
		List<Venta> ventas = builder.build().filtrar(lista);
		assertEquals( 2, ventas.size() );
		assertTrue( ventas.contains(venta1) );
		assertTrue( ventas.contains(venta3) );
		assertTrue( ventas.contains(venta4) );
	}

	@Test
	public void testAnd() throws ParametrosMalColocadosException {
		//Verifico el contenido
		BuilderFiltroVenta builder = BuilderFiltroVenta.filtroEntreMontos(100f, 1000f).
				and(BuilderFiltroVenta.filtroConEnvios());
		List<Venta> ventas = builder.build().filtrar(lista);
		assertEquals( 1, ventas.size() );
		assertTrue( ventas.contains(venta1) );
	}

	@Test
	public void testOr() throws ParametrosMalColocadosException {
		//Verifico el contenido
		BuilderFiltroVenta builder = BuilderFiltroVenta.filtroEntreMontos(100f, 1000f).
				or(BuilderFiltroVenta.filtroConEnvios());
		List<Venta> ventas = builder.build().filtrar(lista);
		assertEquals( 3, ventas.size() );
		assertTrue( ventas.contains(venta1) );
		assertTrue( ventas.contains(venta5) );
		assertTrue( ventas.contains(venta4) );
	}

}
